Skip to content

S04-01 Web-DOM

[TOC]

概述

API

API(Application Programming Interface,应用程序编程接口):是一组预定义的函数、协议和工具,用于构建/集成应用程序,隐藏内部实现细节,仅暴露必要交互接口。

  • 任何开发语言都有自己的API
  • API的特征输入和输出(I/O)
    • var max = Math.max(1, 2, 3);
  • API的使用方法(console.log('adf'))

Web API

Web API:是浏览器提供的一组预定义接口,允许 JS 代码与浏览器功能、硬件设备或系统服务交互,从而赋予网页动态操作能力和高级功能。它是 Web 开发的核心基础,使开发者能够突破静态页面的限制,实现复杂的交互和数据通信。

常见API分类

  1. 文档对象模型:操作 HTML/CSS 结构

    • DOM API
  2. 网络通信:与服务器交换数据

    • Fetch API
    • XMLHttpRequest
  3. 客户端存储:在浏览器中持久化数据

    • Web Storage
    • IndexedDB
  4. 多媒体:图形绘制、音频处理

    • Canvas
    • Web Audio API
  5. 设备交互:访问硬件传感器

    • Geolocation
    • Device Orientation
  6. 异步任务管理:处理耗时操作

    • Promise
    • Web Workers

JS的组成

JS组成:JavaScript由以下三个部分组成:

  • ECMAScript:JavaScript 的语法和基础功能标准,由 ECMA International 组织维护(最新版本为 ES2025)。
  • DOM:将 HTML/XML 文档结构化为树状对象模型,提供操作页面内容的 API。
  • BOM:浏览器提供的与窗口、导航、屏幕等交互的非标准化 API。

image-20250426131857678

DOM

概念

DOM(Document Object Model,文档对象模型,文档树模型):是浏览器将 HTML/XML 文档解析为的结构化对象模型,它允许JS程序动态访问和操作页面内容、结构与样式。

DOM本质

  • 内存表示:HTML 文档被浏览器解析为树状数据结构,每个标签、属性、文本都成为树中的节点(Node)
  • 编程接口:提供 JavaScript 可调用的 API,实现页面交互(如点击事件、内容更新)。
  • 动态关联:DOM 与渲染引擎实时同步,修改 DOM 会触发浏览器重绘/重排。

DOM Tree

DOM Tree:一个页面不只是有html、head、body元素,也包括很多子元素,这些元素最终会在HTML结构中形成一个树结构。在抽象成DOM对象的时候,它们也会形成一个树结构,该结构就叫DOM Tree。

image-20250425223349748

DOM关系图

DOM相当于是JS和HTML、CSS之间的桥梁,通过浏览器提供给我们的DOM API,我们可以对元素以及其中的内容做任何事。

BOM

BOM(Browser Object Model,浏览器对象模型):是浏览器提供的与当前窗口或标签页交互的对象集合,允许开发者控制浏览器行为(如导航、窗口尺寸、历史记录等)。它与 DOM(文档对象模型)相辅相成,共同构成 Web 前端开发的核心能力。

对比DOM

维度BOMDOM
作用对象浏览器窗口、导航、历史等网页文档内容(HTML/XML)
标准化无统一标准(但现代浏览器行为趋同)W3C 标准规范
核心对象windowlocationhistorydocument、元素节点
应用场景控制浏览器行为操作页面内容

核心对象

  1. window:顶级对象,所有全局变量和函数都是 window 的属性。
  2. navigator:浏览器信息。
  3. location:URL 管理。
  4. history:浏览历史。
  5. screen:屏幕信息。

document

概述

documentDocument,是浏览器环境中全局对象 window 的属性,代表当前加载的网页文档,是 DOM 的入口点。它提供了访问和操作页面内容的核心 API,是前端开发最基础且重要的对象。

核心作用

  1. DOM访问入口:整个 HTML 文档的根节点。
  2. 页面元数据管理:标题、URL、Cookie 等。
  3. 动态内容操作:增删改查元素、样式、事件。
  4. 资源加载控制:脚本、样式表、图片的加载。

API-Document

属性

  • document.documentElementElement只读,返回当前文档的直接子节点。对于 HTML 文档一般代表该文档的 html 元素。
  • document.bodybody|frameset|null,获取或设置当前文档的 bodyframeset节点(不常用设置)。
  • document.headHTMLHeadElement只读,返回当前文档的 head 元素。
  • document.titlestring,获取或设置当前文档的标题
  • document.doctypeDocumentType只读,返回当前文档的文档类型定义DTD<!DOCTYPE html>)。
  • document.imagesHTMLCollection只读,返回当前文档中所包含的图片的 HTMLCollection。
  • document.scriptsHTMLCollection只读,返回包含文档中所有的 script 元素的 HTMLCollection。
  • document.linksHTMLCollection只读,返回一个包含文档中所有超链接的 HTMLCollection。
  • document.formsHTMLCollection只读,返回一个包含当前文档中所有表单元素 form 的 HTMLCollection。
  • document.cookiestring,返回一个使用分号分隔的 cookie 列表,或设置(写入)一个 cookie。

方法

元素查询

元素创建

文本写入

  • document.write()(...content)不建议,用于向 HTML 文档直接写入内容的方法(可能引发副作用)。

DOM操作

导航

节点导航

节点导航:如果我们获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的导航。

常见节点导航 API

适用于处理 所有类型节点(元素、文本、注释等),返回的节点可能包含非元素类型。

  • node.parentNodeNode|null只读,返回一个当前节点的父节点
  • node.previousSiblingNode|null只读,返回与该节点同级的前一个节点
  • node.nextSiblingNode|null只读,返回与该节点同级的下一个节点
  • node.childNodesNodeList只读,返回一个包含了该节点所有子节点动态变化的的NodeList。
  • node.firstChildNode|null只读,返回该节点的第一个子节点
  • node.lastChildNode|null只读,返回该节点的最后一个子节点

image-20250426104821016

元素导航@

常见元素导航 API

专用于 元素节点(Element),自动过滤非元素节点,更符合日常开发需求。

image-20250426112234230

table导航

table:在 DOM 中拥有独特的导航方法,通过特定属性和集合可快速定位行列数据。

常见table导航 API

  • table
  • table.captionHTMLTableCaptionElement|null,获取/设置表格标题元素(如果存在)。
  • table.tHeadHTMLTableSectionElement|null,获取/设置表格的 thead 元素(如果存在)。
  • table.tFootHTMLTableSectionElement|null,获取/设置表格的 tfoot 元素(如果存在)。
  • table.tBodiesHTMLCollection只读,返回包含元素中的所有 tbody动态集合
  • table.rowsHTMLCollection只读,返回包含元素中的所有 tr动态集合
  • row
  • row.cellsHTMLCollection只读,获取行内所有单元格(td,th)动态集合
  • row.sectionRowIndexnumber只读,获取行在表格body区域中的索引
  • row.rowIndexnumber只读,获取行在整个表格中的索引
  • cell
  • cell.cellIndexnumber只读,获取单元格在行中的索引
  • cell.colSpannumber,获取/设置跨列数
  • cell.rowSpannumber,获取/设置跨行数

image-20250426120312009

练习:实现如下效果【

image-20250426141420809

form导航

form:是用户输入的核心组件,DOM 提供了丰富的 API 用于访问和操作表单及其控件。

常见form导航 API

  • document
  • document.formsHTMLCollection只读,返回一个包含当前文档中所有表单元素 form 的 HTMLCollection。
  • form
  • form.elementsHTMLFormControlsCollection只读,返回 form 元素中包含的所有表单控件

访问表单元素控件的方法

  1. 通过索引访问

    js
    const firstElement = form.elements[0]; // 第一个表单控件
  2. 通过 name 或 id 访问

    html
    <form id="myForm">
      <input type="text" name="username" id="user">
      <input type="radio" name="gender" value="male">
      <input type="radio" name="gender" value="female">
    </form>
    js
    const form = document.getElementById('myForm');
    
    // 访问单个元素
    const usernameInput = form.elements.username; // 或 form.elements['username']
    
    // 访问同名元素(如单选按钮)
    const genderRadios = form.elements.gender; // 返回 RadioNodeList
    console.log(genderRadios.value); // 输出选中的值(如 "male")
  3. 使用 namedItem() 方法

    js
    const userElement = form.elements.namedItem('user'); // 通过 id="user" 查找

获取元素

我们想要操作页面上的某部分(显示/隐藏,动画),需要先获取到该部分对应的元素,才能进行后续操作。

传统方法

传统方法

示例

js
const header = document.getElementById('main-header');

const buttons = document.getElementsByClassName('btn primary');

const images = document.getElementsByTagName('img');

现代方法

现代方法

  • document.querySelector() / el.querySelector()(selectors),返回文档中与指定的选择器匹配的第一个元素节点
  • document.querySelectorAll() / el.querySelectorAll()(selectors),返回包含文档中与指定的选择器匹配的所有元素节点的列表。

节点属性

节点常见属性

  • node.nodeTypenumber只读,用于标识节点的类型
  • node.nodeNameDOMString只读,返回当前节点的节点名称。
  • el.tagNamestring只读,返回当前元素的标签名。
  • el.innerHTMLDOMString,获取/设置 HTML 语法表示的元素的后代
  • node.textContentstring|null,返回/设置一个元素内所有子节点及其后代的文本内容
  • node.outerHTMLDOMString,获取/设置 HTML 语法表示的元素以及其后代
  • node.data/nodeValue:``,获取非元素节点的文本内容。

nodeType

node.nodeTypenumber只读,用于标识节点的类型

DOM常见节点类型

类型(Node Type)常量常量值示例
元素节点Node.ELEMENT_NODE1<div>, <p>, <a>
属性节点(已废弃)Node.ATTRIBUTE_NODE2class="content"
文本节点Node.TEXT_NODE3'段落'
注释节点Node.COMMENT_NODE8<!-- 注释 -->
文档节点Node.DOCUMENT_NODE9document 对象
文档类型节点Node.DOCUMENT_TYPE_NODE10<!DOCTYPE html>

示例

  1. 判断元素节点

    js
    const element = document.getElementById("header");
    if (element.nodeType === Node.ELEMENT_NODE) { // 或 element.nodeType === 1
      console.log("这是一个元素节点");
    }
  2. 过滤文本节点

    html
    <div id="content">Hello <!-- 注释 --> World</div>
    js
    const div = document.getElementById("content");
    div.childNodes.forEach(node => {
      if (node.nodeType === Node.TEXT_NODE) {
        console.log("文本内容:", node.textContent); // 输出 "Hello " 和 " World"
      }
    });

元素操作

创建元素

  • document.write()(...content)不建议,用于向 HTML 文档直接写入内容的方法(可能引发副作用)。
  • el.innerHTMLstring读取或修改 HTML 元素内容的属性。可以操作元素内部的 HTML 结构(包括标签和文本)。
  • document.createElement()(tagName,options?),用给定标签名创建一个新的元素,支持自定义标签。

插入元素@

  • el.append()(...nodesOrStrings),用于向 DOM 节点的子节点列表末尾插入一个或多个节点或字符串的方法。
  • el.prepend()(...nodesOrStrings),用于向 DOM 节点的子节点列表开头插入一个或多个节点或字符串的方法。
  • el.before()(...nodesOrStrings),用于在指定节点的前面插入一个或多个节点或字符串的方法。
  • el.after()(...nodesOrStrings),用于在指定节点的后面插入一个或多个节点或字符串的方法。
  • el.replaceWith()(...nodesOrStrings),用于替换 DOM 节点的方法,它可以将当前节点替换为一个或多个新节点或字符串。

image-20250428151118908

公共特性

  • 不会解析HTML标签:字符串参数会被隐式转换为 TextNode。
  • 移动已有节点:如果传入的节点已存在于文档中,会先被移除原位置再插入到新位置。
  • 性能优化:操作多个节点时,优先使用 DocumentFragment 减少重排次数。

示例

  1. 基础用法

    js
    const list = document.createElement("ul");
    
    // append()
    const lastItem = document.createElement('li')
    lastItem.textContent = 'lastItem'
    list.append(lastItem)
    
    // prepend()
    const lastItem = document.createElement('li')
    firstItem.textContent = 'firstItem'
    list.append(firstItem)
    
    // before()
    const prevSibling = document.createElement('div')
    prevSibling.textContent = 'prevSibling'
    list.before(prevSibling)
    
    // after()
    const nextSibling = document.createElement('p')
    nextSibling.textContent = 'nextSibling'
    list.after(nextSibling)
    
    // replaceWith()
    const olEl = document.createElement('ol')
    list.replaceWith(olEl)

移除元素

el.remove()(),用于从 DOM 树中移除当前元素节点的方法。直接作用于目标元素,无需通过父节点操作。

克隆元素

node.cloneNode()(deep?),用于克隆 DOM 节点的方法,可生成当前节点的副本。通过参数控制是否深度复制子节点,适用于需要复用或动态生成相似结构的场景。

传统元素操作方法

在很多地方我们也会看到一些旧的操作方法:

公共特性

  • 移动已有节点:如果传入的节点已存在于文档中,会先被移除原位置再插入到新位置。
  • 性能优化:操作多个节点时,优先使用 DocumentFragment 减少重排次数。
  • 仅支持单个节点:每次只能添加一个节点,若需插入多个节点,需多次调用或使用 DocumentFragment。
  • 参数必须为节点对象:若传入非节点对象(如字符串、数字),会抛出 TypeError。
  • 必须是直接子节点:参数中的节点必须是parentNode的直接子节点,否则会抛出 DOMException。
  • 旧节点的处理:被替换/移除的 oldNode/childNode 仍然存在于内存中,可重新插入到 DOM 或其他位置。
  • 事件监听器处理:被替换/移除的 oldNode/childNode 的事件监听器不会自动移除,需手动解绑。

示例

  1. 基础用法

    js
    const parent = document.createElement("div");
    
    // appendChild()
    const lastChild = document.createElement("p");
    lastChild.textContent = "lastChild";
    parent.appendChild(lastChild);
    
    // insertBefore()
    const insertChild = document.createElement("span");
    insertChild.textContent = "insertChild";
    parent.insertChild(insertChild, lastChild);
    
    // replaceChild()
    const replaceChild = document.createElement("strong");
    replaceChild.textContent = "replaceChild";
    parent.replaceChild(replaceChild, insertChild);
    
    // removeChild()
    parent.removeChild(lastChild);

案例

动态创建列表

image-20250428170829648

时间显示

image-20250428172149727

image-20250428172210969

image-20250428172411496

倒计时

image-20250428175252562

image-20250428175218927

尺寸位置滚动

元素尺寸位置滚动

在 JS 中,获取和操作元素的尺寸、位置及滚动状态是前端开发中常见的需求。

元素尺寸

属性计算方法
clientWidth / clientHeight内容 + padding (不包含滚动条)
offsetWidth / offsetHeight内容 + padding + border (包含滚动条)
scrollWidth / scrollHeight可见内容 + 滚动出去的内容

元素位置

属性计算方法
offsetLeft / offsetTop与当前元素最近的定位父级元素之间的距离
scrollLeft / scrollTop盒子内容滚动出去的距离(包括边框)

其他

属性计算方法
clientLeft / clientTop左/上边框的宽度

image-20250428155348036

685291-20210416171703466-2017962228

685291-20210416171720197-1362067265

685291-20210416171757511-1488219875

window尺寸滚动

在 JS 中,window 对象提供了与**浏览器窗口(视口)**尺寸及滚动状态相关的属性和方法。

window尺寸

属性计算方法
window.innerWidth / innerHeight布局视口宽度/高度(包含垂直滚动条的宽度)
window.outerWidth / outerHeight浏览器窗口外部的宽度/高度。
document.documentElement.clientWidth / clientHeightHTML元素的client宽度/高度
document.documentElement.scrollWidth / scrollHeightHTML元素的scroll宽度/高度

window滚动

属性计算方法
window.scrollX / scrollY文档水平/垂直滚动的像素数
window.pageXOffset / pageYOffset等同于 window.scrollX / scrollY

滚动方法

  • el.scrollTo() / window.scrollTo()(x, y)({left,top,behavior}),用于控制可滚动元素滚动位置的方法。适用于overflow设置为 scroll 或 auto 的元素。
  • el.scrollBy() / window.scrollBy()(x, y)({left,top,behavior}),用于控制可滚动元素相对当前位置滚动的方法。
  • el.scrollIntoView()(alignToTop)({behavior,block,inline}),用于将元素滚动到浏览器窗口或可滚动容器可视区域的方法。

image-20250428165519073

属性

元素属性 Attribute

元素属性(Attribute):是添加到 HTML 标签中的额外信息,用于定义元素的行为、样式、数据或其他特性。属性以键值对的形式存在(如 name="value"),为元素提供丰富的功能扩展。

语法格式

html
<标签名 属性1="值1" 属性2="值2">内容</标签名>
<!-- 示例 -->
<a href="https://example.com" target="_blank">访问示例</a>

特性

  • 大小写不敏感
  • 属性值为字符串类型

元素属性的分类

  • 标准Attribute

    • 全局属性
    • 功能属性
    • 事件处理器属性
  • 非标准Attribute

全局属性

全局属性:是所有 HTML 元素共有的属性;它们可以用于所有元素,即使属性可能对某些元素不起作用。

常见全局属性

  • id唯一标识符,用于CSS或JavaScript精准选择元素。
  • class:为元素指定一个或多个类名(用空格分隔),用于CSS样式或JavaScript选择。
  • titleJS:el.title,提供额外信息,通常显示为工具提示
  • styleJS:el.style,内联CSS样式(优先级高)。
  • data-*JS:el.dataset,存储自定义数据(*为自定义名称),通过JavaScript的dataset访问。
  • hiddenJS:el.hidden,隐藏元素(等效于display: none)。
  • draggableJS:el.draggable,控制元素是否可拖动(需配合拖放API)。
表单元素属性【
  • value 用于大部分表单元素的内容获取(option除外)
  • type 可以获取input标签的类型(输入框或复选框等)
  • disabled 禁用属性
  • checked 复选框选中属性
  • selected 下拉菜单选中属性

元素对象属性 Property

通用属性操作

通用属性方法

通用属性方法支持所有的attribute的访问,包括标准和非标准的属性:

  • el.attributesNamedNodeMap只读,返回一个 NamedNodeMap 对象,其中包含相应 HTML 元素的指定属性。
  • el.getAttribute()(attributeName),从当前节点获取指定属性的值,并以字符串形式返回。
  • el.setAttribute()(name, value)设置当前节点的指定属性值,若属性不存在则创建。
  • el.hasAttribute()(attributeName),返回一个布尔值,表示元素是否具有指定属性
  • el.removeAttribute()(attrName),从当前节点删除指定属性。

示例

  1. 获取所有属性

    html
    <a id="link" href="https://example.com" target="_blank">链接</a>
    js
    const link = document.getElementById('link');
    const attrs = link.attributes;
    
    // 输出所有属性及值
    Array.from(attrs).forEach(attr => {
      console.log(`${attr.name}: ${attr.value}`);
    });
    // 输出:
    // id: link
    // href: https://example.com
    // target: _blank
  2. 访问指定属性

    js
    const link = document.querySelector('a');
    const href = link.getAttribute('href'); // 返回链接地址(字符串)
  3. 设置指定属性

    js
    const img = document.querySelector('img');
    img.setAttribute('alt', '产品图片'); // 设置 alt 属性
  4. 检查属性存在

    js
    const checkbox = document.querySelector('input[type="checkbox"]');
    if (checkbox.hasAttribute('checked')) {
      console.log('复选框默认选中');
    }
  5. 移除指定属性

    js
    const input = document.querySelector('input');
    input.removeAttribute('disabled'); // 移除禁用状态

标准属性操作

对于标准attribute,会在DOM对象上创建与其对应的property属性:

image-20250428112734700

Property对比Attribute

  • 大多数情况下,它们是相互作用的:
    • 改变property,通过attribute获取的值会随之变化
    • 改变attribute,property对应的值也会随之变化
  • 除非特殊情况,设置/获取属性推荐使用property的方式,因为它默认是有类型提示的。

image-20250426181226560

类名 className/classList

操作class的方法:

方法一:通过 className 操作

缺点:会覆盖之前的class

示例

js
var box = document.getElementById('box');
box.className = 'show';

方法二:通过 classList 操作(推荐)

如果需要添加或移除单个class,可以使用classList属性。

示例

  1. 基本使用

    html
    <div class="foo"></div>
    js
    const div = document.createElement("div");
    
    // 1. 移除
    div.classList.remove("foo"); // <div class=""></div>
    
    // 2. 添加
    div.classList.add("anotherclass"); // <div class="anotherclass"></div>
    
    // 3. 切换
    div.classList.toggle("visible"); // <div class="anotherclass visible"></div>
    
    // 4. 是否存在
    console.log(div.classList.contains("foo")); // false
  2. 高级使用

    js
    // 1. 添加或移除多个类值
    div.classList.add("foo", "bar", "baz");
    div.classList.remove("foo", "bar", "baz");
    
    // 2. 使用展开语法添加或移除多个类值
    const cls = ["foo", "bar"];
    div.classList.add(...cls);
    div.classList.remove(...cls);
    
    // 3. 将类值 "foo" 替换成 "bar"
    div.classList.replace("foo", "bar");
样式 style
基本使用

htmlElement.styleCSSStyleDeclaration,:获取/设置元素的样式属性。可直接操作元素的内联样式

语法格式

js
// 1. 获取样式值
const value = element.style.propertyName;

// 2. 设置样式值
element.style.propertyName = "value";

// 3. 批量设置样式(覆盖原有内联样式)
element.style.cssText = "property1: value1; property2: value2;";

// 4. 使用 setProperty() 方法(支持连字符属性名)
element.style.setProperty("property-name", "value");

核心特性

  • 驼峰式命名转换:CSS 属性名在 JavaScript 中需转换为驼峰式命名。
  • 仅操作内联样式:仅控制元素的内联样式(即 style 属性定义的样式),不影响通过 CSS 类或外部样式表定义的样式。
  • 值必须为字符串:即使数值类型(如 opacity、zIndex),也需转换为字符串,需要带单位(如px)。

示例

js
const box = document.getElementById("box");

// 1. 设置背景色和宽度
box.style.backgroundColor = "#f00";
box.style.width = "200px";

// 2. 使用 setProperty 设置边框
box.style.setProperty("border", "2px solid black");

// 3. 使用 cssText 批量设置样式
element.style.cssText = "color: red; margin: 10px;";
getComputedStyle()

通过style只能读取到内联样式,但是style和css文件中的样式是读取不到的。此时可以通过 getComputedStyle() 来读取。

window.getComputedStyle()(element,pseudoEl?)只读,用于获取元素最终计算后的 CSS 样式的方法。

设置多个样式@

方式一:style.cssText,可以通过修改 style.cssText 属性来一次性设置多个 CSS 样式

js
// 获取元素
const element = document.getElementById("myElement");

// 一次性修改多个样式
element.style.cssText = "background-color: red; font-size: 20px; color: white;";

方式二:setAttribute(),可以通过 setAttribute 方法设置 style 一次性修改多个样式

js
// 获取元素
const element = document.getElementById("myElement");

// 一次性修改多个样式
element.setAttribute("style", "background-color: red; font-size: 20px; color: white;");

方式三:Object.assign(),可以通过JS的 Object.assign() 方法将多个样式合并到元素的 style 属性中。

js
// 获取元素
const element = document.getElementById("myElement");

// 使用 Object.assign 一次性设置多个样式
Object.assign(element.style, {
  backgroundColor: 'red',
  fontSize: '20px',
  color: 'white'
});
自定义属性 dataset

el.datasetDOMStringMap,用于访问和操作 HTML 元素的自定义数据属性(data-* 的属性。

语法格式

js
// 获取元素的 dataset 对象
const data = element.dataset;

// 读取属性值
const value = element.dataset.keyName;

// 设置属性值
element.dataset.keyName = "newValue";

// 删除属性
delete element.dataset.keyName;

核心特性

  • 属性名转换规则:HTML 中的 data-* 属性名会自动转换为驼峰式命名(如 data-user-iduserId)。
  • 值的隐式转换:所有值均为字符串,需手动转换其他类型。设置非字符串值时会自动转为字符串。

示例

  1. 存储和读取复杂数据(JSON)

    html
    <div data-config='{"theme": "dark", "lang": "en"}'></div>
    
    <script>
      const div = document.querySelector("div");
      const config = JSON.parse(div.dataset.config);
      console.log(config.theme); // "dark"
    </script>
  2. 动态更新 UI 状态

    html
    <button data-click-count="0" onclick="incrementCount(this)">点击次数:0</button>
    
    <script>
      function incrementCount(button) {
        const count = parseInt(button.dataset.clickCount) + 1;
        button.dataset.clickCount = count.toString();
        button.textContent = `点击次数:${count}`;
      }
    </script>

事件

概念

认识事件

事件(Event):是用户或浏览器自身执行的某种动作的抽象表示。例如:点击按钮、滚动页面、按下键盘、加载完成一个图片等。事件是 JS 实现交互性的核心机制,允许代码对用户或系统的行为作出响应

事件的核心概念

  • 事件类型(Event Type)
  • 事件目标(Event Target)
  • 事件处理函数(Event Handler)
  • 事件对象(Event Object)

事件处理方式

常见的事件处理方式有以下3种:

方式一:HTML属性,直接在 HTML 元素标签中通过 on[event] 属性绑定事件处理函数。

html
<button onclick="alert('Clicked!')">点击</button>

特点

  • 简单直接:适合快速测试。
  • 代码耦合:HTML 与 JavaScript 混杂,难以维护。
  • 全局污染:依赖全局函数,易引发命名冲突。

方式二:DOM属性,通过 JavaScript 为 DOM 元素的 on[event] 属性赋值。

js
const button = document.querySelector('button');
button.onclick = function() {
  console.log('Clicked!');
};

特点

  • 简单易用:快速绑定单个事件。
  • 单监听器限制:同一事件只能绑定一个处理函数(后绑定的会覆盖之前的)。
  • 兼容性好:所有浏览器支持。

方式三:addEventListener(推荐,使用 addEventListener 方法绑定事件监听器,支持多个监听器和更精细的控制。

js
const button = document.querySelector("button");

function handleClick(event) {
  console.log("点击事件触发", event.target);
}

// 绑定事件
button.addEventListener("click", handleClick);

// 移除事件
button.removeEventListener("click", handleClick);

特点

  • 多监听器支持:可绑定多个处理函数,按注册顺序执行。
  • 灵活控制阶段:通过 capture 参数指定事件阶段(捕获或冒泡)。
  • 更安全的移除:需通过 removeEventListener 移除,需保持函数引用一致。

事件流

事件流(Event Flow):定义了事件如何在 DOM 树中传递,以及何时触发绑定在元素上的事件处理函数。

事件流的三个阶段:事件流分为三个阶段,按顺序依次执行:

  1. 捕获阶段(Capture Phase)
    • 事件从 window 对象开始,逐级向下传播到目标元素的父级,直到到达目标元素本身。
    • 目的:允许在事件到达目标前拦截和处理事件。
    • 默认不监听:除非显式指定,否则大多数事件监听器不会在此阶段触发。
  2. 目标阶段(Target Phase)
    • 事件到达目标元素(触发事件的元素)。
    • 如果事件监听器绑定在目标元素上,无论它是监听捕获还是冒泡阶段,都会在此阶段触发。
  3. 冒泡阶段(Bubble Phase)
    • 事件从目标元素开始,逐级向上冒泡回 window 对象。
    • 默认监听阶段:大多数事件(如 click)默认在冒泡阶段触发监听器。

image-20250505164139794

指定事件流监听阶段:通过 addEventListener 的第三个参数(useCapture)控制事件触发阶段。

  • true:在捕获阶段触发监听器。
  • false(默认值):在冒泡阶段触发监听器。
js
// 捕获阶段触发
document.getElementById("parent").addEventListener("click", () => {
  console.log("父元素 - 捕获阶段");
}, true);

// 冒泡阶段触发
document.getElementById("child").addEventListener("click", () => {
  console.log("子元素 - 冒泡阶段");
}, false);

事件流的兼容性与历史

  • 早期浏览器差异
    • IE8 及更早版本仅支持冒泡阶段。
    • Netscape 支持捕获阶段,但未被广泛采用。
  • 标准化:W3C 统一了事件流模型,规定先捕获后冒泡。

事件对象 event

事件对象(Event Object):是事件触发时自动创建的对象,包含与事件相关的详细信息和方法。通过事件处理函数的参数可以访问该对象,用于控制事件行为和获取上下文数据。

当一个事件发生时,就会有和这个事件相关的很多信息,如事件类型、点击的元素、点击的位置等。这些信息会被封装到一个由浏览创建的event对象中。

访问方式:event对象会在传入的事件处理函数回调时被系统传入,我们可以在回调函数中访问该event对象

image-20250506103824032

常见属性方法【

属性

方法

image-20250505170959925

事件目标

事件目标(Event Target):指代触发事件的 DOM 元素。通过事件对象 event 的 target 属性可访问它,用于精确识别事件源并实现灵活的交互逻辑。

event.targetEventTarget,触发事件的元素(如用户直接点击的子元素)。

html
<div id="parent">
  <button id="child">点击</button>
</div>

<script>
  document.getElementById("parent").addEventListener("click", function(event) {
    console.log(event.target); // 点击按钮时输出 <button id="child">
  });
</script>

event.currentTargetEventTarget,绑定事件处理函数的元素(等同于 this)。

js
document.querySelector("#parent").addEventListener("click", function(event) {
  console.log(event.currentTarget); // 输出 <div id="parent">
  console.log(this === event.currentTarget); // true
});

示例

  1. 事件委托

    通过父元素监听子元素事件,利用 event.target 识别具体触发元素,优化性能

    html
    <ul id="list">
      <li>Item 1</li>
      <li>Item 2</li>
    </ul>
    
    <script>
      document.getElementById("list").addEventListener("click", function(event) {
        if (event.target.tagName === "LI") {
          console.log("点击了:", event.target.textContent);
        }
      });
    </script>
  2. 动态交互

    直接操作触发事件的元素(如修改样式、获取数据属性)

    js
    document.querySelector("button").addEventListener("click", function(event) {
      const target = event.target;
      target.style.backgroundColor = "red"; // 修改点击按钮的背景色
      console.log(target.dataset.info);    // 获取自定义属性 data-info 的值
    });
  3. 阻止默认行为或传播

    根据事件目标执行条件性控制

    js
    document.querySelector("a").addEventListener("click", function(event) {
      if (event.target.href.includes("blocked")) {
        event.preventDefault(); // 阻止特定链接跳转
      }
    });

阻止事件默认行为

event.preventDefault()(),用于阻止事件的默认行为(如表单提交、链接跳转)。

阻止事件传播

event.stopPropagation()(),用于阻止事件在 DOM 树中继续传播(包括捕获和冒泡阶段)。

this

事件处理函数中的 this 指向根据绑定方式的不同而变化:

  1. 默认指向绑定事件的元素

    当通过 addEventListener 或 DOM 属性(如 onclick) 绑定事件时,this 默认指向 触发事件的元素。

    js
    const btn = document.getElementById("btn");
    
    // DOM 属性绑定
    btn.onclick = function() {
      console.log(this); // 输出:<button id="btn">点击</button>
    };
    
    // addEventListener 绑定
    btn.addEventListener("click", function() {
      console.log(this); // 输出:<button id="btn">点击</button>
    });
  2. 箭头函数中的 this 指向

    箭头函数 没有自己的 this,它会继承外层作用域的 this。在事件处理中,可能导致 this 指向 window 或 undefined(严格模式)。

    html
    <button id="btn">点击</button>
    
    <script>
      const btn = document.getElementById("btn");
      
      btn.addEventListener("click", () => {
        console.log(this); // 输出:window(非严格模式)或 undefined(严格模式)
      });
    </script>
  3. 事件委托中的 this 指向

    在事件委托中,this 指向 绑定事件的父元素,而 event.currentTarget 与 this 相同。event.target 则是实际触发事件的子元素。

    html
    <ul id="list">
      <li>Item 1</li>
      <li>Item 2</li>
    </ul>
    
    <script>
      document.getElementById("list").addEventListener("click", function(event) {
        console.log(this); // 输出:<ul id="list">...</ul>
        console.log(event.currentTarget === this); // true
        console.log(event.target); // 输出:被点击的 <li> 元素
      });
    </script>

EventTarget

常见方法

  • eventTarget.addEventListener()(type, listener, options? | useCapture?),用于为 DOM 元素绑定事件监听器的核心方法,支持精细控制事件触发的阶段、频率及生命周期。
  • eventTarget.removeEventListener()(type, listener, options? | useCapture?),用于移除通过 addEventListener 绑定的事件监听器的方法。正确使用它可以避免内存泄漏和重复事件触发问题。
  • eventTarget.dispatchEvent()(event),用于手动触发自定义或原生事件的方法。

事件委托

事件委托(Event Delegation):是一种利用事件冒泡机制,将子元素的事件监听统一委托给父元素处理,并通过 event.target 识别实际触发事件的子元素执行对应的逻辑,它是一种设计模式。

实现步骤

  1. 绑定事件到父元素

    js
    parent.addEventListener("eventType", handler);
  2. 识别触发子元素

    js
    function handler(event) {
      const target = event.target; // 触发事件的子元素
    }
  3. 筛选目标元素并处理

    js
    if (target.matches("selector")) {
      // 执行针对该子元素的逻辑
    }

示例

  1. 列表项点击事件

    html
    <ul id="list">
      <li>Item 1</li> <!-- 点击它 -->
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
    
    <script>
      document.getElementById("list").addEventListener("click", function(event) {
        const li = event.target.closest("li"); // 确保点击的是 li 或其子元素
        if (li) {
          console.log("点击了:", li.textContent);
        }
      });
    </script>
  2. 列表项排他点击事件

    image-20250505231351584

    image-20250506150430603

  3. 动态添加按钮处理

    html
    <div id="container">
      <!-- 动态添加的按钮 -->
    </div>
    
    <script>
      document.getElementById("container").addEventListener("click", function(event) {
        if (event.target.classList.contains("action-btn")) {
          console.log("按钮被点击:", event.target.dataset.action);
        }
      });
    
      // 动态添加按钮
      const newBtn = document.createElement("button");
      newBtn.className = "action-btn";
      newBtn.dataset.action = "delete";
      newBtn.textContent = "删除";
      document.getElementById("container").appendChild(newBtn);
    </script>

常见事件

常见事件

鼠标事件

  • click:鼠标点击元素(按下并释放左键)。
  • dblclick:鼠标双击元素。
  • contextmenu:右键点击元素(弹出上下文菜单前)。
  • mousedown / mouseup:鼠标按下 / 释放。
  • mousemove:鼠标在元素内移动。
  • mouseenter / mouseleave不冒泡,鼠标进入 / 离开元素本身。仅在进入目标元素时触发一次,子元素之间移动不触发。
  • mouseover / mouseout冒泡,鼠标进入 / 离开元素或其子元素。鼠标在元素和子元素之间移动时会重复触发。

键盘事件

  • keydown:按下键盘任意键。
  • keyup:松开键盘按键。

表单事件

  • submit:表单提交。
  • reset:表单重置。
  • input:输入框内容实时变化。
  • change:表单元素值变化并失焦(如输入框、下拉框)。
  • focus / blur:元素获得 / 失去焦点。

窗口事件

  • DOMContentLoaded:HTML 解析完成(无需等待样式、图片)。
  • load:页面及资源(如图片)加载完成。
  • beforeunload:窗口关闭或刷新前。
  • resize:窗口大小变化。
  • scroll:页面滚动。

过渡事件

  • transitionend:CSS 过渡结束播放。

触摸事件

  • touchstart:触摸开始。
  • touchmove:触摸移动。
  • touchend:触摸结束。

剪切板事件

  • copy / paste:复制 / 粘贴内容。

媒体事件

  • play / pause:媒体播放 / 暂停。

案例

消息滚动切换

image-20250506175710588

image-20250506175825767

京东M站头部关闭

image-20250506180355958